package com.youtills.resource;

import com.youtills.core.lang.ObjectUtil;

/**
 * TODO - !!!doc me!!!
 *
 * @author ravi.chiluveru
 * @since 23.0 (8/15/14)
 */
public final class ResourceAttributeValue<T>{

    private final Resource resource;
    private final Attribute<T> attribute;
    private T value;
    private boolean nullable;
    private boolean immutable;

    public ResourceAttributeValue(Resource resource, Attribute<T> attribute) {
        this(resource, attribute, true, null);
    }

    public ResourceAttributeValue(Resource resource, Attribute<T> attribute, boolean nullable, T defaultValue) {
        assert (resource != null ) : "resource cannot be null.";
        assert (attribute != null ) : "attribute cannot be null.";

        String attributeName = attribute.getName();
        assert (!"resourceId".equalsIgnoreCase(attributeName) && !"resourceId".equalsIgnoreCase(attributeName)) :
                attributeName + " is a reserved attribute name.";

        this.resource = resource;
        this.attribute = attribute;
        this.nullable = nullable;
        this.value = defaultValue;

        if(resource instanceof BaseResource) {
            ((BaseResource)resource).addAttribute(this);
        }
    }

    public String getName() {
        return attribute.getName();
    }

    Attribute getAttribute() {
        return attribute;
    }

    public boolean setValue(T value) {

        if(isImmutable() || (!nullable && value == null)){
            return false;
        }
        /*
        Set the value only if it's not the same instance.
        Thought of checking .equals instead of ==.
        But there may be chance that equals may returns true but those instances are different.
        So just check for == only. Not .equals.
        */
        if(!isImmutable()) {
            T currentValue = this.value;
            this.value = value;
            boolean modified = !ObjectUtil.areEquals(this.value, currentValue);
            if(modified && resource instanceof BaseResource){
                ((BaseResource)resource).addModifiedAttribute(this);
                ((BaseResource)resource).setDirty(true);
            }
            return modified;
        }

        return false;
    }

    public T getValue() {
        return this.value;
    }

    public boolean isModified() {
        return false;
    }

    public boolean isImmutable() {
        return immutable || resource.isImmutable();
    }

    @Override
    public String toString() {
        return String.valueOf(this.value);
    }
}
